level: beginner
::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::
What is Eddie?
Eddie is a new code editor written in Liberty BASIC, for Liberty BASIC. Eddie can be customized and extended to meet the needs of the individual programmer. The newest version is available here: Eddie v6
Making the Filedialogs User-Friendly
A filedialog allows the user to select the filename of a file on disk. Your program can then open the file for reading or writing. The syntax for the filedialog command is as follows:
filedialog "Title Here","template here",filename$
The title is something like "Open" or "Save As" depending on the use for the filedialog. The template is the names and types of files that will be displayed. (More on this later.) The receiver variable, called filename$ in the example above, contains the name of the file chosen by the user, or an empty string if the user cancelled.
The filedialog always opens in the DefaultDir$ unless you tell it where to start. You can do this by modifying the template. A typical template looks like this:
filedialog "Open","*.txt;*.bas",filename$
The asterisk is a wildcard character. The template in this code -- "*.txt;*.bas" -- causes all files with the extensions "txt" and "bas" to be displayed in the file box of the filedialog. You can use as many of these in the template as you like. Separate the items in the list with semicolons. To open files of all types, the template looks like this: "*.*".
A Path for the Filedialog
You can cause the filedialog to open in any folder, just by altering the template string. To cause it to open in "c:\" do this:
filedialog "Open","c:\",filename$
To cause it to open in "c:\" and show only text files:
filedialog "Open","c:\*.txt",filename$
To cause it to open in "c:\" in the folder "My Progs" and to look for BAS files:
filedialog "Open","c:\myprogs\*.bas",filename$
You can use variables in the template:
folder$ = "Program Files\" driveLetter$ = "c:\" template$ = "*.*" filedialog "Open",driveLetter$ + folder$ + template$,filename$
Did you notice that the backslash characters are needed? Try the code, but remove the backslash characters in the variables and see what happens.
To make the filedialog in Eddie more user-friendly, we'll keep track of the most recent directory chosen by the user, and add it to the template for all filedialogs. That way, the user won't have to navigate all over his drive and directory structure to get back to his favorite folder each time he wants to open or save a file. To accomplish this, we need to separate the path from the filename each time the user accesses a filedialog. We'll store the path in a variable called "Curdir$" to use in the template for filedialogs.
Extracting the Path from a Filename
To get the path from a complete filename, we'll use the following function, called "SeparatePath$." Here's how it works. It loops through the filename string, starting at the last character and moving forward. Using the MID$() string function, it evaluates each character in turn. If the character is not a backslash, it continues to the next character. The position in the filename is kept in the variable "fileindex" and it is decremented each time through the loop. When it reaches a character that evaluates to a backslash, it stops and exits the loop. The "fileindex" variable now holds the location of the backslash. The function returns the leftmost characters, with a length equal to the value of "fileindex".
Function SeparatePath$(f$)
fileindex=Len(f$)
filelength=Len(f$)
While Mid$(f$, fileindex,1)<>"\"
fileindex=fileindex-1
Wend
SeparatePath$=Left$(f$,fileindex)
End Function
Adding the Current Directory to Filedialogs
So far, we know how to cause a filedialog to open in a specified directory, and we know how to extract the path from a complete filename. Now we must put these two things together in Eddie. First, let's initialize the "Curdir$" variable at the start of the program.
Curdir$ = DefaultDir$
Now, each time we use a filedialog, we use it as part of the template.
filedialog "Save As",Curdir$ + "\*.bas",savefile$
Everytime the user selects a filename with a filedialog, we update the contents of the Curdir$ variable.
Curdir$ = SeparatePath$(savefile$)
Eddie's users will be very happy now!
Adding to the INI File
In Eddie, we can run liberty.exe for the user, but we also want to run the tokenized version of Freeform with run402.exe, and we want to be able to open the Liberty BASIC helpfile. We don't want to ask the user for these paths each time, so we'll ask him once, and store the information in the INI file, along with the path to Liberty BASIC. We'll store each bit of information on its own line. Here's how we'll write the data to a file:
[writeIniFile]
open IniFile$ for output as #f
print #f, LibertyExe$ 'path to liberty.exe
print #f, Freeform$ 'path to freeform.tkn
print #f, Runtime$ 'path to run402.exe
print #f, Help$ 'path to liberty.hlp
close #f
RETURN
EOF, or "How to avoid crashing Eddie!"
We discussed how to check for the INI file existence, and prompt the user to specify the path to liberty.exe in a previous issue. We use the same methods to get the paths to the runtime engine, Freeform, and the helpfile, and we write all of this information into the INI file.
We need to read the INI file each time that Eddie opens. It isn't enough to check for the file's existence any more. If we try to read past the end of the file, the program will halt with an error. This happens when we try to INPUT from a file when there is no more data. To catch this error, we check to see if we've reached the end of the file before each INPUT statement. We do this with the EOF(#file) function. The function returns "0" if we haven't yet reached the end of the file. Once we've determined that the file length is greater than 0, we can read the first bit of data. We discussed this in a previous issue. The first line in the INI file contains the path to Liberty BASIC. After we retrieve this information, we check to see if we've reached the end of the file. If we haven't, we INPUT the next bit of data, and store it in the "Freeform$" variable:
if eof(#f) = 0 then line input #f, Freeform$
We do the same check for end of file before attempting to INPUT the path to the runtime engine, and then the path to the helpfile.
[readIniFile]
timer 0
open IniFile$ for append as #f
lenFile = LOF(#f)
close #f
'if the length = 0, the file didn't exist, so get info from user
if lenFile=0 then
notice "To use Eddie successfully, you must set the path to Liberty.exe, Freeform, the runtime engine and the helpfile."
gosub [GetLibertyPath]
gosub [GetFreeformPath]
gosub [GetRuntimePath]
gosub [GetHelpPath]
else
'if the length is greater than 0, the file exists
'open it for input and read the first line into LibertyExe$
open IniFile$ for input as #f
line input #f, LibertyExe$
'read second line into Freeform$, only if end of file not reached
if eof(#f) = 0 then line input #f, Freeform$
'read third line into Runtime$, only if end of file not reached
if eof(#f) = 0 then line input #f, Runtime$
'read fourth line into Help$, only if end of file not reached
if eof(#f) = 0 then line input #f, Help$
close #f
end if
TIMER 50, [activateTimer]
RETURN
Getting the Paths from the User
Each of the subs to get a path to the desired version of Liberty BASIC, Freeform, etc. looks like this one:
[GetLibertyPath]
timer 0
filedialog "Find Liberty.exe",Curdir$ + "\*liberty*.exe",LibertyExe$
if LibertyExe$="" then
notice "Liberty.exe not found."
else
Curdir$ = SeparatePath$(LibertyExe$)
end if
TIMER 50, [activateTimer]
RETURN
A filedialog allows the user to select the path. It includes the Curdir$ as part of the filename template. If the user cancels the dialog, he is notified that Eddie can't find the file. If the user selects a file, the Curdir$ variable is updated for its next use.
When the program closes, we save the path information to the ini file:
[writeIniFile]
open IniFile$ for output as #f
print #f, LibertyExe$
print #f, Freeform$
print #f, Runtime$
print #f, Help$
close #f
RETURN
Running Freeform - Using Runxx.Exe
Tokenized files that were created with the GOLD version of Liberty BASIC can be run using the runtime engine. We've asked the user to specify the path to freeform and to the runtime engine, and we can now use the RUN command to run the Freeform TKN.
Here is the routine to run Freeform. We check to see if the user has specified a path to both Freeform.TKN and to the runtime engine. If both paths exist, we use the RUN command to run Freeform with the runtime engine:
[freeform]
if Freeform$ = "" then gosub [GetFreeformPath]
if Freeform$ = "" then wait
if Runtime$ = "" then gosub [GetRuntimePath]
if Runtime$="" then wait
run Runtime$ + " " + chr$(34) + Freeform$ + chr$(34)
wait
Running a Windows Helpfile (*.hlp)
For Eddie to be really useful, it should allow the user to access the Liberty BASIC helpfile with a simple mouse click. In the code above, we've asked the user to specify the path to the helpfile. Now, all we have to do is run it for him! It couldn't be easier! It requires the use of the RUN command, which was discussed at length in a previous version of Eddie and in other newsletter articles. We need to know which application to call in order to run a helpfile, and the required application is "winhlp32.exe." Here's the code to run the helpfile:
run "winhlp32 " + chr$(34) + Help$ + chr$(34)
Eddie v6
Here is the code for Eddie v6.
'** Eddie - an LB Code Editor
'version 6
'expands ini file, reads at startup, writes at close
'check for EOF when reading ini file with line input
'adds menu to run user's favorite version of Freeform
'adds runtime engine and freeform.tkn path to ini file
'invokes LB help from help menu, adds help path to ini file
'runs LB help with winhlp32
'separates path from filename and stores in Curdir$
'keep Curdir$ variable, use in filedialog, renew with each filedialog
if val(Version$)<4.02 then
notice "For Liberty BASIC v4.02 and higher."
end
end if
[VariableSetup]
'#f will be the handle for opening files
'note that Version$ is a reserved variable name
EddieVersion$ = "6"
LibertyExe$ = "" 'path to Liberty.exe for running programs:
Freeform$ = "" 'path to Freeform
Runtime$ = "" 'path to runtime engine, run402.exe
Help$ = "" 'path to LB help
IniFile$ = "eddie6.ini" 'name of ini file for this version of Eddie
Curdir$ = DefaultDir$
text$ = "" 'variable to hold contents of texteditor
tempfile$ = DefaultDir$ + "\tempfile.bas" 'filename for writing a temp file to disk
savefile$ = "" 'filename for user saving file to disk
dim branch$(1000) 'array to hold branch labels
branch$(1) = "" 'first designation takes you to top of code
gosub [readIniFile]
[WindowSetup]
NOMAINWIN
WindowWidth = 600 : WindowHeight = 360
UpperLeftX = INT((DisplayWidth-WindowWidth)/2)
UpperLeftY = INT((DisplayHeight-WindowHeight)/2)
[ControlSetup]
'menus
Menu #1, "&File", "&New", [new], "&Open", [open], "&Save", [save],_
|,"&Print", [print], "E&xit", [quit]
'LB will place Edit menu where you specify in list of menus
'do not use ampersand & in Edit menu caption
Menu #1, "Edit"
Menu #1, "&Run","R&un", [run]
Menu #1, "&Tools", "&Go to Line", [gotoLine], "&Freeform", [freeform]
Menu #1, "&Help", "He&lp", [help], "&About", [about]
'buttons for a toolbar lookalike
button #1.new, "New",[new],UL, 0, 0, 45, 20
button #1.open, "Open",[open],UL, 45, 0, 45, 20
button #1.save, "Save",[save],UL, 90, 0, 45, 20
button #1.print, "Print",[print],UL, 135, 0, 45, 20
button #1.cut, "Cut",[cut],UL, 190, 0, 45, 20
button #1.copy, "Copy",[copy],UL, 235, 0, 45, 20
button #1.paste, "Paste",[paste],UL, 280, 0, 45, 20
button #1.run, "Run",[run],UL, 335, 0, 45, 20
button #1.help, "Help",[help],UL, 380, 0, 45, 20
'a graphicbox for line numbers
stylebits #1.g, 0,_WS_BORDER,0,0
graphicbox #1.g, 0, 52, 39, 230
'a texteditor
texteditor #1.te, 39, 52, 545, 250
'combobox for branch labels
stylebits #1.c, 0,_CBS_AUTOHSCROLL,0,0
combobox #1.c, branch$(),[chooseBranch],40,22,385,300
Open "Eddie - an LB Code Editor v." + EddieVersion$ for Window as #1
'trap the close event, if the user closes the
'window with the X button
#1 "trapclose [quit]"
'set the font for the window and controls
#1 "font ms_sans_serif 10"
'set a custom font for the texteditor
#1.te "!font courier_new 0 16"
'set up the graphicbox
#1.g "down; fill Buttonface; backcolor buttonface; flush"
'use the same font as the texteditor
#1.g "font courier_new 0 16"
#1.g "autoresize"
'cause the texteditor to resize automatically
'when the user resizes the window
#1.te "!autoresize"
'select item in combobox
#1.c "selectindex 1"
TIMER 50, [activateTimer] 'initialize the timer
[loop] Wait
[quit]
gosub [writeIniFile]
TIMER 0
close #1 : END
[activateTimer]
'check the number of lines of text
#1.te "!lines rowcount"
'if it has changed, refill branch label combobox
if rowcount<>lastRowcount then gosub [fillBranch]
'new lastRowcount is this rowcount
lastRowcount=rowcount
'check the number of the row at the top of the texteditor
#1.te "!origin? col row"
'if it is the same as last check, do nothing
if row = lastRow then wait
'turn off timer for this routine
TIMER 0
'new lastRow is current row, for next check
lastRow = row
'delete previously named drawing segment called *linenums*
#1.g "delsegment linenums"
'location to start drawing line numbers
#1.g "place 2 14"
'fill with default buttonface color to erase previous line numbers
#1.g "fill buttonface"
'use top row number as starting number for line numbers
for i = row to row+100
'using() aligns the numbers for us
num$=using("####",i)
#1.g "\";num$
next
'flush and give this segment the name *linenums*
#1.g "flush linenums"
'start timer again
TIMER 50, [activateTimer]
wait
[new] #1.te "!CLS" : Wait
[open]
TIMER 0
filedialog "Open",Curdir$ + "\*.bas",openfile$
if openfile$="" then wait
Curdir$ = SeparatePath$(openfile$)
open openfile$ for input as #f
#1.te "!contents #f"
close #f
gosub [fillBranch]
TIMER 50, [activateTimer]
Wait
[save]
TIMER 0
filedialog "Save As",Curdir$ + "\*.bas",savefile$
if savefile$="" then wait
'make sure that user had given the file a *bas extension
if right$(lower$(savefile$),4)<>".bas" then
savefile$=savefile$+".bas"
end if
#1.te "!contents? text$"
if text$="" then
notice "No file to save."
wait
end if
Curdir$ = SeparatePath$(savefile$)
open savefile$ for output as #f
#f text$
close #f
TIMER 50, [activateTimer]
Wait
[print]
TIMER 0
#1.te "!contents? text$"
if text$="" then
notice "No text to print."
wait
end if
lprint text$ 'send text to printer
dump 'cause printing to happen now
TIMER 50, [activateTimer]
Wait
[help]
if Help$ = "" then gosub [FindHelpPath]
if Help$ = "" then wait
run "winhlp32 " + chr$(34) + Help$ + chr$(34)
Wait
[about] Wait
[cut] #1.te "!cut" : wait
[copy] #1.te "!copy" : wait
[paste] #1.te "!paste" : wait
[run]
TIMER 0
if LibertyExe$ = "" then gosub [GetLibertyPath]
'if we can't find the path to Liberty BASIC
'we must abort the RUN operation
if LibertyExe$="" then wait
#1.te "!contents? text$"
if text$="" then
notice "No code to run."
wait
end if
open tempfile$ for output as #f
print #f, text$
close #f
RUN LibertyExe$ + " " + chr$(34) + tempfile$ + chr$(34)
TIMER 50, [activateTimer]
wait
[freeform]
if Freeform$ = "" then gosub [GetFreeformPath]
if Freeform$ = "" then wait
if Runtime$ = "" then gosub [GetRuntimePath]
if Runtime$="" then wait
run Runtime$ + " " + chr$(34) + Freeform$ + chr$(34)
wait
[GetLibertyPath]
timer 0
filedialog "Find Liberty.exe",Curdir$ + "\*liberty*.exe",LibertyExe$
if LibertyExe$="" then
notice "Liberty.exe not found."
else
Curdir$ = SeparatePath$(LibertyExe$)
end if
TIMER 50, [activateTimer]
RETURN
[GetRuntimePath]
timer 0
filedialog "Find runtime engine",Curdir$ + "\*run*.exe",Runtime$
if Runtime$="" then
notice "Runtime engine not found."
else
Curdir$ = SeparatePath$(Runtime$)
end if
TIMER 50, [activateTimer]
RETURN
[GetFreeformPath]
timer 0
filedialog "Find Freeform",Curdir$ + "\*.tkn",Freeform$
if Freeform$="" then
notice "Freeform not found."
else
Curdir$ = SeparatePath$(Freeform$)
end if
TIMER 50, [activateTimer]
RETURN
[GetHelpPath]
timer 0
filedialog "Find Help",Curdir$ + "\*.hlp",Help$
if Help$="" then
notice "Help not found."
else
Curdir$ = SeparatePath$(Help$)
end if
TIMER 50, [activateTimer]
RETURN
[readIniFile]
timer 0
open IniFile$ for append as #f
lenFile = LOF(#f)
close #f
'if the length = 0, the file didn't exist, so get info from user
if lenFile=0 then
notice "To use Eddie successfully, you must set the path to Liberty.exe, Freeform, the runtime engine and the helpfile."
gosub [GetLibertyPath]
gosub [GetFreeformPath]
gosub [GetRuntimePath]
gosub [GetHelpPath]
else
'if the length is greater than 0, the file exists
'open it for input and read the first line into LibertyExe$
open IniFile$ for input as #f
line input #f, LibertyExe$
'read second line into Freeform$, only if end of file not reached
if eof(#f) = 0 then line input #f, Freeform$
'read third line into Runtime$, only if end of file not reached
if eof(#f) = 0 then line input #f, Runtime$
'read fourth line into Help$, only if end of file not reached
if eof(#f) = 0 then line input #f, Help$
close #f
end if
TIMER 50, [activateTimer]
RETURN
[writeIniFile]
open IniFile$ for output as #f
print #f, LibertyExe$
print #f, Freeform$
print #f, Runtime$
print #f, Help$
close #f
RETURN
[fillBranch]
timer 0
#1.te "!lines rowcount"
#1.te "!origin? col row"
REDIM branch$(rowcount)
branch$(1)="" + space$(300) + "1"
'fill combobox with labels, functions and sub definitions
'pad with 300 spaces, then add line number
bx=2
For i=1 to rowcount
#1.te,"!line ";i;" line$";
If Left$(Trim$(line$),1)="[" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
If Lower$(Word$(line$,1))="function" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
If Lower$(Word$(line$,1))="sub" Then
branch$(bx)=Trim$(line$) + space$(300) + str$(i)
bx=bx+1
End If
Next i
#1.c "reload"
#1.c "selectindex 1";
TIMER 50, [activateTimer]
return
[chooseBranch]
'** CHOOSE BRANCH, SET EDITOR TO THAT POSITION
timer 0
#1.c "selection? branchselect$"
'get the text at the end of the item
ln$ = right$(branchselect$, 6)
'get value of text, which will = linenumber
linenumber = val(ln$)
'now scroll texteditor so that line is at top
#1.te "!origin 1 ";linenumber
#1.te "!setfocus"
TIMER 50, [activateTimer]
Wait
[gotoLine]
timer 0
#1.te "!lines rowcount"
gotoLine=rowcount
msg$="Go to line? Max is ";gotoLine
prompt msg$;gotoLine
if gotoLine>rowcount then gotoLine=rowcount
if gotoLine<1 then gotoLine=1
#1.te "!origin 1 ";gotoLine
#1.te "!setfocus"
TIMER 50, [activateTimer]
wait
Function SeparatePath$(f$)
fileindex=Len(f$)
filelength=Len(f$)
While Mid$(f$, fileindex,1)<>"\"
fileindex=fileindex-1
Wend
SeparatePath$=Left$(f$,fileindex)
End Function
::::::::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::